import os
import glob
import cv2
import numpy as np
import subprocess  # 导入 subprocess
import signal  # 用于在 Windows 上终止进程
from typing import List, Dict, Any
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QMessageBox, QSizePolicy
from PyQt5.QtGui import QPixmap, QImage, QColor
from PyQt5.QtCore import Qt, QTimer, QSize


class BattlesView(QWidget):
    def __init__(self, battles: List[Dict[str, Any]]):
        super().__init__()
        self._battles = sorted(battles, key=lambda b: b.get("year") or 0)
        self._current = 0
        self._phase = 0

        # --- OpenCV 和音频进程相关 ---
        self.cap = None
        self.audio_process = None  # 用于存储 ffmpeg 子进程
        self.video_timer = QTimer(self)
        self.video_timer.timeout.connect(self._update_video_frame)
        self.current_media_path = ""
        self.is_paused = False

        # --- UI 布局 ---
        layout = QVBoxLayout(self)
        self.title = QLabel("重大战役")
        self.title.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.title)

        self.image = QLabel()
        self.image.setAlignment(Qt.AlignCenter)
        self.image.setMinimumHeight(200)

        self.video_label = QLabel()
        self.video_label.setAlignment(Qt.AlignCenter)
        self.video_label.setMinimumHeight(240)
        self.video_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        layout.addWidget(self.image)
        layout.addWidget(self.video_label)
        self.video_label.hide()

        self.content = QLabel()
        self.content.setWordWrap(True)
        layout.addWidget(self.content)

        controls = QHBoxLayout()
        self.btn_prev = QPushButton("上一场")
        self.btn_next = QPushButton("下一场")
        self.btn_play = QPushButton("开始回放")
        self.btn_prev.clicked.connect(self.prev)
        self.btn_next.clicked.connect(self.next)
        self.btn_play.clicked.connect(self.play)
        for b in [self.btn_prev, self.btn_next, self.btn_play]:
            controls.addWidget(b)
        layout.addLayout(controls)

        self._update_view()

    def _update_view(self):
        self._stop_media()

        if not self._battles:
            self.content.setText("暂无战役数据")
            self.video_label.hide()
            self.image.show()
            return

        b = self._battles[self._current]
        self.current_media_path = self._resolve_media(b)
        print(f"--- 尝试加载媒体: {self.current_media_path} ---")

        if self._is_video(self.current_media_path):
            self.image.hide()
            self.video_label.show()
            self.btn_play.setText("暂停")
            self.is_paused = False

            # --- 使用 OpenCV 打开视频 ---
            self.cap = cv2.VideoCapture(self.current_media_path)
            if not self.cap.isOpened():
                print(f"错误: OpenCV 无法打开视频文件 {self.current_media_path}")
                QMessageBox.critical(self, "播放错误", f"无法打开视频文件:\n{self.current_media_path}")
                self._stop_media()
                self._show_fallback_image(b)
                return

            # --- 使用 subprocess 启动 ffmpeg 播放音频 ---
            self._start_audio_process(self.current_media_path)

            self.video_timer.start(30)  # 约33FPS

        else:
            self.video_label.hide()
            self.image.show()
            self.btn_play.setText("开始回放")
            if os.path.exists(self.current_media_path):
                pix = QPixmap(self.current_media_path)
            else:
                print(f"图片文件未找到: {self.current_media_path}")
                pix = self._create_error_pixmap()

            if not pix.isNull():
                self.image.setPixmap(pix.scaled(640, 360, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation))
            else:
                self.image.setText("无法加载图片")

        self.content.setText(
            f"{b.get('name', '')}（{b.get('year', '')}）\n地点：{b.get('location', '')}\n{b.get('summary', '')}")

    def _start_audio_process(self, video_path):
        """启动一个独立的 ffmpeg 进程来播放音频"""
        try:
            # 构建 ffmpeg 命令
            # -i: 输入文件
            # -vn: 禁用视频流
            # -acodec pcm_s16le: 将音频解码为 PCM 格式，方便输出
            # -f wav: 输出格式为 WAV
            # - |: 管道，将输出传递给下一个命令
            # ffmpeg -i input.mp4 -vn -acodec pcm_s16le -f wav - | ffplay -
            # 在 Windows 上，直接用 ffplay 更简单
            cmd = [
                'ffplay',
                '-nodisp',  # 不显示视频窗口
                '-autoexit',  # 播放完毕后自动退出
                video_path
            ]
            print(f"启动音频进程: {' '.join(cmd)}")
            # 在 Windows 上，需要设置 creationflags 来隐藏控制台窗口
            self.audio_process = subprocess.Popen(cmd, creationflags=subprocess.CREATE_NO_WINDOW)
            print("音频进程启动成功。")
        except FileNotFoundError:
            print("错误: 未找到 'ffplay' 命令。请确保 ffmpeg 的 bin 目录在系统 PATH 中。")
            QMessageBox.critical(self, "播放错误", "未找到 ffplay，无法播放音频。\n请检查 ffmpeg 安装。")
        except Exception as e:
            print(f"启动音频进程时出错: {e}")

    def _update_video_frame(self):
        """只更新视频帧"""
        if not self.cap or not self.cap.isOpened():
            return

        ret, frame = self.cap.read()
        if not ret:
            print("视频播放结束。")
            self._stop_media()
            self.btn_play.setText("重新播放")
            return

        rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        qt_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qt_image)
        self.video_label.setPixmap(pixmap.scaled(self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def _stop_media(self):
        """停止视频和音频进程"""
        print("正在停止所有媒体...")
        self.is_paused = True
        if self.video_timer.isActive():
            self.video_timer.stop()
        if self.cap:
            self.cap.release()
            self.cap = None

        # --- 终止音频进程 ---
        if self.audio_process:
            try:
                print("正在终止音频进程...")
                # 在 Windows 上，使用 taskkill 来强制结束进程树
                subprocess.run(['taskkill', '/F', '/T', '/PID', str(self.audio_process.pid)], check=False,
                               creationflags=subprocess.CREATE_NO_WINDOW)
                self.audio_process.wait(timeout=2)
                print("音频进程已终止。")
            except Exception as e:
                print(f"终止音频进程时出错: {e}")
            finally:
                self.audio_process = None

        if self.video_label:
            self.video_label.clear()
            self.video_label.hide()
        if self.image:
            self.image.show()
        self.current_media_path = ""
        print("媒体已停止。")

    def _show_fallback_image(self, battle: Dict[str, Any]):
        self.image.show()
        self.video_label.hide()
        media_path = self._resolve_media(battle)
        base_path = os.path.splitext(media_path)[0]
        for ext in ['.png', '.jpg', '.jpeg', '.bmp']:
            image_path = base_path + ext
            if os.path.exists(image_path):
                print(f"视频播放失败，找到回退图片: {image_path}")
                pix = QPixmap(image_path)
                self.image.setPixmap(pix.scaled(640, 360, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation))
                self.content.setText(
                    f"{battle.get('name', '')}（{battle.get('year', '')}）\n地点：{battle.get('location', '')}\n{b.get('summary', '')}\n\n(视频无法播放，已显示静态图片)")
                return

        print("未找到回退图片，显示占位符。")
        self.image.setPixmap(
            self._create_error_pixmap().scaled(640, 360, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation))
        self.content.setText(
            f"{battle.get('name', '')}（{battle.get('year', '')}）\n地点：{battle.get('location', '')}\n{b.get('summary', '')}\n\n(视频无法播放，且未找到备用图片)")

    def _create_error_pixmap(self):
        pix = QPixmap(200, 150)
        pix.fill(QColor("#8e1616"))
        return pix

    def prev(self):
        if not self._battles:
            return
        self._current = (self._current - 1) % len(self._battles)
        self._update_view()

    def next(self):
        if not self._battles:
            return
        self._current = (self._current + 1) % len(self._battles)
        self._update_view()

    def play(self):
        if not self._battles:
            return

        b = self._battles[self._current]
        media_path = self._resolve_media(b)

        if self._is_video(media_path):
            if self.cap and self.video_timer.isActive() and not self.is_paused:
                # --- 暂停 ---
                print("用户点击暂停。")
                self.is_paused = True
                self.video_timer.stop()
                # 暂停 ffplay 比较复杂，我们直接停止它
                if self.audio_process:
                    self.audio_process.terminate()
                    self.audio_process = None
                self.btn_play.setText("继续播放")
            else:
                # --- 播放/继续 ---
                if self.cap and self.cap.isOpened():
                    print("用户点击播放/继续。")
                    if self.is_paused:
                        # 从暂停状态恢复，重新启动音频进程
                        self._start_audio_process(self.current_media_path)
                        self.is_paused = False
                    else:
                        # 首次播放或重新播放
                        self._update_view()
                        return
                    self.video_timer.start(30)
                    self.btn_play.setText("暂停")
                else:
                    self._update_view()
        else:
            self._phase = 0
            if not hasattr(self, 'image_timer'):
                self.image_timer = QTimer(self)
                self.image_timer.timeout.connect(self._tick)
            self.image_timer.start(33)

    def _tick(self):
        self._phase += 1
        if self._phase > 150:
            self.image_timer.stop()
            self._update_view()
            return
        dots = '.' * ((self._phase // 10) % 4)
        b = self._battles[self._current]
        self.content.setText(
            f"{b.get('name', '')}（{b.get('year', '')}）\n地点：{b.get('location', '')}\n{b.get('summary', '')}\n回放中{dots}")

    def set_data(self, battles: List[Dict[str, Any]]):
        self._battles = sorted(battles, key=lambda b: b.get("year") or 0)
        self._current = 0
        self._update_view()

    def _resolve_media(self, battle: Dict[str, Any]) -> str:
        src = battle.get("image", "")
        if src and os.path.exists(src):
            return os.path.abspath(src)
        bid = battle.get("id", "")
        search_dir = os.path.join("assets", "battles")
        if not os.path.isdir(search_dir):
            print(f"警告：媒体搜索目录不存在: {os.path.abspath(search_dir)}")
            return ""
        candidates = glob.glob(os.path.join(search_dir, f"{bid}.*"))
        if candidates:
            return os.path.abspath(candidates[0])
        print(f"未找到ID为 '{bid}' 的战役媒体文件。")
        return ""

    @staticmethod
    def _is_video(path: str) -> bool:
        if not path:
            return False
        ext = os.path.splitext(path)[1].lower()
        return ext in {".mp4", ".mov", ".avi", ".mkv", ".wmv", ".m4v"}
